package org.luxor.commons.core.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.databind.type.ArrayType;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;
import org.apache.commons.lang.StringUtils;
import org.luxor.commons.core.exception.BaseException;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

/**
 * Jackson2 工具类
 *
 * @author Mr.Yan  @date: 2020/11/12
 */
public class Jackson2Utils {
    private static final ObjectMapper objectMapper;

    static {
        // 解决long类型精度丢失问题
        objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);

        // 解决BigDecimal类型精度丢失问题
        simpleModule.addSerializer(BigDecimal.class, BigDecimalSerializer.instance);

        objectMapper.registerModule(simpleModule);
        // 设置为空的字段不返回
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 反序列化时候，忽略多余的字段
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 忽略空Bean转Json的错误
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 指定json转换时间类型的时区
        objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        // 指定返回的时间格式
        objectMapper.setDateFormat(new SimpleDateFormat(DateUtils.DATE_TIME_PATTERN));
    }

    public static ObjectMapper getObjectMapper() {
        return objectMapper;
    }

    /**
     * 对象转换为JsonText
     */
    public static String toJson(Object object) {
        if (object == null) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(object);
        } catch (IOException e) {
            throw new BaseException(JsonMappingException.fromUnexpectedIOE(e).getMessage());
        }
    }

    /**
     * 对象转换为格式化的JsonText
     */
    public static String toJsonPretty(Object object) {
        return StringUtils.replace(toJson(object), "\t", "  ");
    }

    /**
     * JsonText转换为JavaBean
     */
    public static <T> T toBean(String jsonText, Class<T> clazz) {
        if (StringUtils.isBlank(jsonText) || clazz == null) {
            return null;
        }
        try {
            return objectMapper.readValue(jsonText, clazz);
        } catch (IOException e) {
            throw new BaseException(JsonMappingException.fromUnexpectedIOE(e).getMessage());
        }
    }

    /**
     * JsonText转换为JavaBean
     */
    public static <T> T toBean(InputStream content, Class<T> clazz) {
        if (content == null || clazz == null) {
            return null;
        }
        try {
            return objectMapper.readValue(content, clazz);
        } catch (IOException e) {
            throw new BaseException(JsonMappingException.fromUnexpectedIOE(e).getMessage());
        }
    }

    /**
     * 转换为数组
     */
    public static <T> T[] toArray(String jsonText, Class<T> clazz) {
        if (StringUtils.isBlank(jsonText) || clazz == null) {
            return null;
        }
        try {
            ArrayType arrayType = objectMapper.getTypeFactory().constructArrayType(clazz);
            return objectMapper.readValue(jsonText, arrayType);
        } catch (IOException e) {
            throw new BaseException(JsonMappingException.fromUnexpectedIOE(e).getMessage());
        }
    }

    /**
     * 转换为List
     */
    public static <T> List<T> toList(String jsonText, Class<T> clazz) {
        if (StringUtils.isBlank(jsonText) || clazz == null) {
            return null;
        }
        try {
            CollectionType collectionType = objectMapper.getTypeFactory().constructCollectionType(List.class, clazz);
            return objectMapper.readValue(jsonText, collectionType);
        } catch (IOException e) {
            throw new BaseException(JsonMappingException.fromUnexpectedIOE(e).getMessage());
        }
    }

    /**
     * 转换为Map
     */
    public static <T> Map<String, T> toMap(String jsonText, Class<T> clazz) {
        if (StringUtils.isBlank(jsonText)) {
            return new HashMap<>(0);
        }
        try {
            MapType mapType = objectMapper.getTypeFactory().constructMapType(Map.class, String.class, clazz);
            return objectMapper.readValue(jsonText, mapType);
        } catch (IOException e) {
            throw new BaseException(JsonMappingException.fromUnexpectedIOE(e).getMessage());
        }
    }

    /**
     * 转换为JSON对象
     */
    public static JsonNode toJsonObject(String jsonText) {
        try {
            return objectMapper.readTree(jsonText);
        } catch (IOException e) {
            throw new BaseException(JsonMappingException.fromUnexpectedIOE(e).getMessage());
        }
    }

    /**
     * 转换为JSON对象
     */
    public static JsonNode toJsonObject(Object object) {
        try {
            return objectMapper.readTree(objectMapper.writeValueAsString(object));
        } catch (IOException e) {
            throw new BaseException(JsonMappingException.fromUnexpectedIOE(e).getMessage());
        }
    }

    /**
     * BigDecimal序列化
     */
    public static class BigDecimalSerializer extends ToStringSerializer {
        public final static BigDecimalSerializer instance = new BigDecimalSerializer();

        public BigDecimalSerializer() {
            super(Object.class);
        }

        public BigDecimalSerializer(Class<?> handledType) {
            super(handledType);
        }

        @Override
        public boolean isEmpty(SerializerProvider prov, Object value) {
            if (value == null) {
                return true;
            }
            String str = ((BigDecimal) value).stripTrailingZeros().toPlainString();
            return str.isEmpty();
        }

        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider provider)
                throws IOException {
            gen.writeString(((BigDecimal) value).stripTrailingZeros().toPlainString());
            // 如果要求所有BigDecimal保留两位小数，可以这么写：
            // gen.writeString(((BigDecimal) value).setScale(2, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString());
        }

        @Override
        public void serializeWithType(Object value, JsonGenerator gen,
                                      SerializerProvider provider, TypeSerializer typeSer)
                throws IOException {
            // no type info, just regular serialization
            serialize(value, gen, provider);
        }
    }
}
